package com.example.lib_ipc.core;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;

import com.example.lib_ipc.ClassId;
import com.example.lib_ipc.UserBinderInterface;
import com.example.lib_ipc.bean.RequestBean;
import com.example.lib_ipc.bean.RequestParameter;
import com.example.lib_ipc.cache.CacheCenter;
import com.example.lib_ipc.service.UserServiceManager;
import com.google.gson.Gson;

import org.w3c.dom.Text;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class UserBinderIPC {
    private static UserBinderIPC mInstance;
    private CacheCenter mCacheCenter = CacheCenter.getInstance();
    //需要Build才能使用
    UserBinderInterface mUserBinderInterface;

    public static UserBinderIPC getInstance() {
        if (mInstance == null) {
            mInstance = new UserBinderIPC();
        }
        return mInstance;
    }

    private Context mAppContext;

    public void init(Context context) {
        this.mAppContext = context.getApplicationContext();
    }

    //1.服务注册
    public void register(Class<?> clazz) {
        //注册交给CacheCenter 记录
        mCacheCenter.register(clazz);
    }

    public void open(Context context) {
        init(context);
        open(context, null);
    }

    public void open(Context context, String packageName) {
        bind(context, packageName, UserServiceManager.class);
    }

    public void bind(Context context, String packageName, Class<? extends UserServiceManager> service) {
        Intent intent = null;
        if (TextUtils.isEmpty(packageName)) {
            //app 内启动
            intent = new Intent(context, service);
        } else {
            //跨app启动
            //action 启动
            ComponentName componentName = new ComponentName(packageName, service.getName());
            intent = new Intent();
            intent.setComponent(componentName);
            //service.getName = <action android:name="com.example.lib_ipc.service.UserServiceManager" />
            intent.setAction(service.getName());
        }

        context.bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e("UserBinderIPC", "onServiceConnected name" + name);
                //创建进程通信链接
                mUserBinderInterface = UserBinderInterface.Stub.asInterface(service);
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                //1.启动失败回调 2.连接后断开回调
                Log.e("UserBinderIPC", "onServiceDisconnected name" + name);
            }

            @Override
            public void onBindingDied(ComponentName name) {
                Log.e("UserBinderIPC", "onBindingDied name" + name);

            }
        }, Context.BIND_AUTO_CREATE);
    }

    //-----------------------------服务发现，提供给另外一个进程调用--------------------------
    public <T> T getInstance(Class<T> clazz, Object... parameters) {
        //1.通知主进程创建需要的对象
        //1.1 提供给主进程创建对象需要的数据
        sendRequest(clazz, null, parameters, UserServiceManager.TYPE_GET);
        //2.对象返回给B进程
        return getProxy(clazz);
    }

    private <T> T getProxy(Class<T> clazz) {

        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Log.e("UserBinderIPC", "返回给B进程的代理对象 method:" + method.getName());
                //在这里通过AIDL发消息给主进程调用代理需要调用的方法
                String data = sendRequest(clazz, method, args, UserServiceManager.TYPE_INVOKE);
                //获取主进程返回的数据，转化为调用对象（如果有的话）
                //data == null 说明方法是 set
                //data ！= null 说明方法是get
                if (!TextUtils.isEmpty(data)) {
                    Object o = new Gson().fromJson(data, method.getReturnType());
                    return o;
                }
                return null;
            }
        });
    }

    /**
     * @param clazz      调用哪个class
     * @param method     如果method == null ：实例化对象 调用的class的哪个方法
     * @param parameters 方法对应的参数名 和 参数值
     * @param <T>
     * @return 返回的数据：
     * 参数是基本类型：
     * {
     * "className": "UserManager",
     * "methodName": "getUser",
     * "requestParamters": [{
     * "parameterClassName": "java.lang.String",
     * "parameterValue": “weng”
     * }],
     * "type": 2
     * }
     * <p>
     * 参数是对象：
     * {
     * "className": "UserManager",
     * "methodName": "getUser",
     * "requestParamters": [{
     * "parameterClassName": "java.lang.UserInfo",
     * "parameterValue": “{“value”}”
     * }],
     * "type": 2
     * }
     */
    public <T> String sendRequest(Class<T> clazz, Method method, Object[] parameters, int type) {
        //获取要实例化的class名 通过注解，class.getName 获取的是接口名
        String className = clazz.getAnnotation(ClassId.class).value();
        //获取方法名
        String methodName = method == null ? "getInstance" : method.getName();
        RequestParameter[] requestParameters = null;
        if (parameters != null && parameters.length > 0) {
            requestParameters = new RequestParameter[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                Object parameter = parameters[i];
                //获取传递的对象类型 java.layout.Integer String。。。。
                String paramType = parameter.getClass().getName();
                //序列化 参数值，转成json
                String parameterValue = new Gson().toJson(parameter);
                //构建参数类型和参数值
                RequestParameter requestParameter = new RequestParameter(paramType, parameterValue);
                requestParameters[i] = requestParameter;
            }
        }

        RequestBean requestBean = new RequestBean(className, methodName, requestParameters, type);
        String content = new Gson().toJson(requestBean);
        if (mUserBinderInterface != null) {
            try {
                return mUserBinderInterface.request(content);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        return "";
    }

}
